﻿using System;
using System.Collections.ObjectModel;
using NUnit.Framework;
using NLog;
using ScrappyDB.Attributes;
using ScrappyDB.BaseClasses;

namespace ScrappyDB.IntegrationTests.Core
{
    /// <summary>
    ///This is a test class for BasicPropertyTests and is intended
    ///to contain all BasicPropertyTests Unit Tests
    ///</summary>
    [TestFixture]
    public class BasicPropertyTests
    {
        private static readonly Logger log = LogManager.GetCurrentClassLogger();

        public static DateTime TestDate = DateTime.Now.ToUniversalTime();
        public static double TestDouble = 1234.4567890;
        public static int TestInt = 1234;
        public static double TestNegativeDouble = -5678.7654321;
        public static int TestNegativeInt = -5678;
        public static string TestString = "Test123";

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext { get; set; }

        #region Additional test attributes

        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        [TestFixtureSetUp]
        public  void Init()
        {
            Cleanup();
        }

        //Use ClassCleanup to run code after all tests in a class have run
        [TestFixtureTearDown]
        public  void MyClassCleanup()
        {
            //Cleanup();
        }

        //Use TestInitialize to run code before running each test
        //[SetUp]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TearDown]
        //public void MyTestCleanup()
        //{
        //    Cleanup();
        //}

        private static void Cleanup()
        {
            try
            {
                var db = new Db();
                Collection<EntityTestDataTypes> deleteme = db.Query<EntityTestDataTypes>(true);
                foreach (EntityTestDataTypes d in deleteme)
                {
                    db.Delete(d);
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        #endregion

        //[DomainName("TestDomainName"), EntityName("TestEntityName")]
        //private class EntityWithAttributes : DbEntity
        //{
        //    public Guid Id { get; set; }
        //    public string TestValue { get; set; }
        //    public bool Dirty { get; set; }
        //}

        [Test]
        public void SaveLoadRoundTripTest()
        {
            var target = new Db();
            var o = new EntityTestDataTypes();
            Guid testId = Guid.NewGuid();
            o.Id = testId;
            o.TestString = TestString;
            target.SaveChanges(o);

            var actual = target.Find<EntityTestDataTypes>(testId);

            Assert.AreEqual(TestString, actual.TestString);
        }

        [Test]
        public void SaveIntTest()
        {
            var target = new Db();
            var o = new EntityTestDataTypes();
            Guid testId = Guid.NewGuid();
            o.Id = testId;
            o.TestInt = TestInt;
            target.SaveChanges(o);

            var actual = target.Find<EntityTestDataTypes>(testId);

            Assert.AreEqual(TestInt, actual.TestInt);
        }

        [Test]
        public void SaveNegativeIntTest()
        {
            var target = new Db();
            var o = new EntityTestDataTypes();
            Guid testId = Guid.NewGuid();
            o.Id = testId;
            o.TestInt = TestNegativeInt;
            target.SaveChanges(o);

            var actual = target.Find<EntityTestDataTypes>(testId);

            Assert.AreEqual(TestNegativeInt, actual.TestInt);
        }

        [Test]
        public void SaveDoubleTest()
        {
            var target = new Db();
            var o = new EntityTestDataTypes();
            Guid testId = Guid.NewGuid();
            o.Id = testId;
            o.TestDouble = TestDouble;
            target.SaveChanges(o);

            var actual = target.Find<EntityTestDataTypes>(testId);

            Assert.IsTrue(Math.Abs(TestDouble - actual.TestDouble) < 0.001);
        }

        [Test]
        public void RawDoubleTest()
        {
            var db = new Db();
            var o = new EntityTestDataTypes();

            ////////////123456789012d
            o.Id = Guid.NewGuid(); ;
            o.RawDouble = 123456789012d;
            db.SaveChanges(o);
            var actual = db.Find<EntityTestDataTypes>(o.Id, true);
            Assert.AreEqual(o.RawDouble, actual.RawDouble);


            ////////////123456789012e10d
            o.Id = Guid.NewGuid(); ;
            o.RawDouble = 123456789012e10d;
            db.SaveChanges(o);
            actual = db.Find<EntityTestDataTypes>(o.Id, true);
            Assert.AreEqual(o.RawDouble, actual.RawDouble);

            ////////////1.23456789012e10d
            o.Id = Guid.NewGuid(); ;
            o.RawDouble = 1.23456789012e10d;
            db.SaveChanges(o);
            actual = db.Find<EntityTestDataTypes>(o.Id, true);
            Assert.AreEqual(o.RawDouble, actual.RawDouble);

            ////////////1.23456789012e-10d
            o.Id = Guid.NewGuid(); ;
            o.RawDouble = 1.23456789012e-10d;
            db.SaveChanges(o);
            actual = db.Find<EntityTestDataTypes>(o.Id,true);
            Assert.AreEqual(o.RawDouble, actual.RawDouble);

            ////////////-123456789012e10d
            o.Id = Guid.NewGuid(); ;
            o.RawDouble = -123456789012e10d;
            db.SaveChanges(o);
            actual = db.Find<EntityTestDataTypes>(o.Id, true);
            Assert.AreEqual(o.RawDouble, actual.RawDouble);
        }

        [Test,Ignore]
        public void PaddedAndOffsetDoubleTest()
        {
            try
            {
                var db = new Db();
                var o = new EntityTestDataTypes();

                o.Id = Guid.NewGuid();
                ;
                o.TestDouble = 12345e10d;
                db.SaveChanges(o);
                var actual = db.Find<EntityTestDataTypes>(o.Id);

                Assert.AreEqual(o.TestDouble, actual.TestDouble);
            }
            catch (System.ArgumentException)
            {
                Assert.Inconclusive("This is good, but we still need to add better validation");
            }
        }

        [Test]
        public void SaveNegativeDoubleTest()
        {
            Guid testId = Guid.NewGuid();

            var target = new Db();
            var o = new EntityTestDataTypes { Id = testId, TestDouble = TestNegativeDouble };
            target.SaveChanges(o);

            var actual = target.Find<EntityTestDataTypes>(testId);

            Assert.IsTrue(Math.Abs(TestNegativeDouble - actual.TestDouble) < 0.001);
        }

        [Test]
        public void SaveDateTimeTest()
        {
            var target = new Db();
            var o = new EntityTestDataTypes();
            Guid testId = Guid.NewGuid();
            o.Id = testId;
            o.TestDateTime = TestDate;
            target.SaveChanges(o);

            var actual = target.Find<EntityTestDataTypes>(testId);

            //we drop the milliseconds on save to SimpleDB
            //and so we need to check equality to within a second (since milliseconds are dropped)
            Assert.IsTrue((TestDate - actual.TestDateTime) < TimeSpan.FromSeconds(1));
        }

        [Test]
        public void SaveBoolTest()
        {
            var target = new Db();
            var o = new EntityTestDataTypes();

            Guid testId = Guid.NewGuid();
            o.Id = testId;

            o.TestBool = true;
            target.SaveChanges(o);

            var actual = target.Find<EntityTestDataTypes>(testId);

            Assert.AreEqual(true, actual.TestBool);
        }



        #region Nested type: EntityTestDataTypes

        [DomainName("ScrappyDB_UnitTests2"), EntityName("BasicPropertyTests_EntityTestDataTypes")]
        public class EntityTestDataTypes 
        {
            public Guid Id { get; set; }
            public string TestString { get; set; }
            public int TestInt { get; set; }
            public bool TestBool { get; set; }
            public double TestDouble { get; set; }

            [RawNumber]
            public double RawDouble { get; set; }

            public DateTime TestDateTime { get; set; }
        }

        #endregion
    }
}